/*
 * Copyright 2008-2009 Aying.Net Powered by Fuchun.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.aying.util.security;

import java.security.SecureRandom;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.DecoderException;

import net.aying.util.StringUtils;
import net.aying.util.resources.Resources;

/**
 * {@code Crypto} 抽象类的 {@code DES} 加密/解密算法的实现子类。
 * 
 * @author Fuchun
 * @version 1.0, 2009-6-18
 * @see Crypto
 */
public class DESCrypto extends Crypto {

    private static final Log log = LogFactory.getLog(DESCrypto.class);

    /**
     * 构造一个新的 {@code DESCrypto} 实例。
     */
    public DESCrypto() {
        this(DEFAULT_KEY_SPEC_STRING);
    }

    /**
     * 构造一个指定密钥字符串的 {@code DESCrypto} 实例。
     * 
     * @param sKeySpec 指定的密钥字符串。
     */
    public DESCrypto(String sKeySpec) {
        setAlgorithm(DES_ALGORITHM);
        setSecretKeySpec(createSecretKeySpec(sKeySpec));
    }

    /* 创建一个 SecretKeySpec 实例对象。 */
    private SecretKeySpec createSecretKeySpec(String sKeySpec) {
        if (StringUtils.isNullOrEmpty(sKeySpec))
            sKeySpec = Resources.getText(DEFAULT_KEY_SPEC_STRING);
        byte[] keySpecBytes = sKeySpec.getBytes();
        SecretKeySpec secretKey = new SecretKeySpec(keySpecBytes, 0, DESKeySpec.DES_KEY_LEN,
                DES_ALGORITHM);
        return secretKey;
    }

    /**
     * @see net.aying.util.security.Crypto#decryptString(java.lang.String)
     */
    @Override
    protected String decryptString(String encrypttext) {
        String result = null;
        SecureRandom sr = null;
        try {
            sr = SecureRandom.getInstance(DES_ALGORITHM);
        } catch (NoSuchAlgorithmException ex) {
            sr = new SecureRandom();
        }
        try {
            Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(), sr);
            byte[] bytes = Hex.decodeHex(encrypttext.toCharArray());
            byte[] decryptBytes = cipher.doFinal(bytes);
            result = new String(decryptBytes);
        } catch (NoSuchAlgorithmException ex) {
            log.error(Resources.getMessage(NO_SUCH_ALGORITHM, DES_ALGORITHM), ex);
        } catch (NoSuchPaddingException ex) {
            log.error(Resources.getMessage(NO_SUCH_PADDING, DES_ALGORITHM), ex);
        } catch (DecoderException ex) {
            log.error(Resources.getMessage(DECODER_ERROR, encrypttext, "DESCrypto#encrypt"));
        } catch (InvalidKeyException ex) {
            log.error(Resources.getMessage(INVALID_KEY, getSecretKeySpec().toString()), ex);
        } catch (IllegalStateException ex) {
            log.error(Resources.getText(ILLEGAL_STATE), ex);
        } catch (IllegalBlockSizeException ex) {
            log.error(Resources.getText(ILLEGAL_BLOCK_SIZE), ex);
        } catch (BadPaddingException ex) {
            log.error(Resources.getText(BAD_PADDING), ex);
            // 忽略该错误
            // 此异常只用于 Cipher 解密模式
            ex.printStackTrace();
        }
        return result;
    }

    /**
     * @see net.aying.util.security.Crypto#encryptString(java.lang.String)
     */
    @Override
    protected String encryptString(String plaintext) {
        String result = null;
        SecureRandom sr = null;
        try {
            sr = SecureRandom.getInstance(DES_ALGORITHM);
        } catch (NoSuchAlgorithmException ex) {
            sr = new SecureRandom();
        }
        try {
            Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec(), sr);
            byte[] encryptBytes = cipher.doFinal(plaintext.getBytes());
            result = new String(Hex.encodeHex(encryptBytes));
        } catch (NoSuchAlgorithmException ex) {
            log.error(Resources.getMessage(NO_SUCH_ALGORITHM, DES_ALGORITHM), ex);
        } catch (NoSuchPaddingException ex) {
            log.error(Resources.getMessage(NO_SUCH_PADDING, DES_ALGORITHM), ex);
        } catch (InvalidKeyException ex) {
            log.error(Resources.getMessage(INVALID_KEY, getSecretKeySpec().toString()), ex);
        } catch (IllegalStateException ex) {
            log.error(Resources.getText(ILLEGAL_STATE), ex);
        } catch (IllegalBlockSizeException ex) {
            log.error(Resources.getText(ILLEGAL_BLOCK_SIZE), ex);
        } catch (BadPaddingException ex) {
            // 忽略该错误
            // 此异常只用于 Cipher 解密模式
        }
        return result;
    }
}
